﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Aima.Core.Logic.Propositional.Algorithms
{
    using System.Diagnostics;

    using Aima.Core.Logic.Propositional.Parsing;
    using Aima.Core.Logic.Propositional.Parsing.AST;

    public class Model : IPLVisitor {

        Dictionary<string, bool?> h = new Dictionary<string, bool?>();

        public bool? GetStatus(Symbol symbol)
        {
            bool? status;
            h.TryGetValue(symbol.Value, out status);
            return status;
        }

        public bool IsTrue(Symbol symbol) {
            bool? status;
            h.TryGetValue(symbol.Value, out status);
            if (status != null) 
            {
                return ((bool) status);
            }
            return false;
        }

        public bool IsFalse(Symbol symbol) {
            bool? status;
            h.TryGetValue(symbol.Value, out status);
            if (status != null) {
                return !((bool) status);
            }
            return false;
        }

        private bool IsUnknown(Symbol s) {
            bool? status;
            h.TryGetValue(s.Value, out status);
            return (status == null);

        }

        public Model Extend(Symbol symbol, bool? b) {
            var m = new Model();
            return this.Extend(symbol.Value, b);
        }

        public Model Extend(string s, bool? b) {
            var m = new Model();

            foreach(var k in this.h.Keys)
            {
                m.h[k] = h[k];
            }

            m.h[s] = b;
            return m;
        }

        public void Print() {
            foreach(var k in this.h.Keys)
            {
                Debug.Write(k + " = " + h[k] + " ");
            }
            Debug.WriteLine("");
        }

        public bool IsTrue(Sentence clause) {
            object result = clause.Accept(this, null);
            return (result == null) ? false
                    : ((bool) result);
        }

        public bool IsFalse(Sentence clause) {
            object o = clause.Accept(this, null);
            return (o != null) ? ((bool) o) == false : false;
        }

        public bool IsUnknown(Sentence clause) { // TODO TEST WELL
            object o = clause.Accept(this, null);
            return (o == null);
        }

        public Model Flip(Symbol s) {
            if (this.IsTrue(s)) {
                return this.Extend(s, false);
            }
            if (this.IsFalse(s)) {
                return this.Extend(s, true);
            }
            return this;
        }

        public override string ToString() {
            return h.ToString();
        }

        public object VisitSymbol(Symbol s, ISet<Sentence> arg)
        {
            return this.GetStatus(s);
        }

        public object VisitTrueSentence(TrueSentence ts, ISet<Sentence> arg)
        {
            return true;
        }

        public object VisitFalseSentence(FalseSentence fs, object arg) {
            return false;
        }

        public object VisitNotSentence(UnarySentence fs, ISet<Sentence> arg)
        {
            object negatedValue = fs.Negated.Accept(this, null);
            if (negatedValue != null)
            {
                return (bool) negatedValue;
            }
            return null;
        }

        public object VisitBinarySentence(BinarySentence bs, ISet<Sentence> arg)
        {
            object firstValue = bs.First.Accept(this, null);
            object secondValue = bs.Second.Accept(this, null);
            if ((firstValue == null) || (secondValue == null)) { // strictly not
                // true for or/and
                // -FIX later
                return null;
            } else {
                string oper = bs.Operator;
                if (oper.Equals("AND")) {
                    return this.EvaluateAnd((bool) firstValue, (bool) secondValue);
                }
                if (oper.Equals("OR")) {
                    return this.EvaluateOr((bool) firstValue, (bool) secondValue);
                }
                if (oper.Equals("=>")) {
                    return this.EvaluateImplied((bool) firstValue,
                            (bool) secondValue);
                }
                if (oper.Equals("<=>"))
                {
                    return this.EvaluateBiConditional((bool) firstValue,
                            (bool) secondValue);
                }
                return null;
            }
        }

        public object VisitMultiSentence(MultiSentence fs, ISet<Sentence> argd)
        {
            return null;
        }

        public ISet<Symbol> GetAssignedSymbols() {
            ISet<Symbol> set = new HashSet<Symbol>();
            foreach(var pair in h) 
            {
                Symbol key = new Symbol(pair.Key);
                if (!(this.IsUnknown(key))) 
                {
                    set.Add(key);
                }
            }
            return set;
        }

        public bool Matches(string variable, bool? value) {
            if (value == true) {
                return this.IsTrue(new Symbol(variable));
            }
            if (value == false) {
                return this.IsFalse(new Symbol(variable));
            }
            return false;
        }

        private bool EvaluateAnd(bool firstValue, bool secondValue) {
            if ((firstValue.Equals(true))
                    && (secondValue.Equals(true))) {
                return true;
            } else {
                return false;
            }
        }

        private bool EvaluateOr(bool firstValue, bool secondValue) {
            if ((firstValue.Equals(true))
                    || (secondValue.Equals(true))) {
                return true;
            } else {
                return false;
            }
        }

        private bool EvaluateImplied(bool firstValue, bool secondValue) {
            if ((firstValue.Equals(true))
                    && (secondValue.Equals(false))) {
                return false;
            } else {
                return true;
            }
        }

        private bool EvaluateBiConditional(bool firstValue,
                bool secondValue) {
            if (firstValue.Equals(secondValue)) {
                return true;
            } else {
                return false;
            }
        }
    }
}
